<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>WebSocket 详细调试工具</title>
    <style>
      body {
        font-family: Arial, sans-serif;
        margin: 20px;
        background: #f5f5f5;
      }
      .container {
        max-width: 1200px;
        margin: 0 auto;
        background: white;
        padding: 20px;
        border-radius: 8px;
        box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
      }
      .status-panel {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
        gap: 20px;
        margin-bottom: 20px;
      }
      .status-card {
        padding: 15px;
        border: 1px solid #ddd;
        border-radius: 5px;
        background: #f9f9f9;
      }
      .status-card h3 {
        margin: 0 0 10px 0;
        color: #333;
      }
      .log {
        background: #000;
        color: #0f0;
        font-family: "Courier New", monospace;
        padding: 15px;
        height: 400px;
        overflow-y: auto;
        border-radius: 5px;
        margin: 10px 0;
      }
      .controls {
        margin: 20px 0;
        display: flex;
        gap: 10px;
        flex-wrap: wrap;
      }
      button {
        padding: 10px 20px;
        border: none;
        border-radius: 5px;
        cursor: pointer;
        font-size: 14px;
      }
      .btn-primary {
        background: #007bff;
        color: white;
      }
      .btn-danger {
        background: #dc3545;
        color: white;
      }
      .btn-success {
        background: #28a745;
        color: white;
      }
      .btn-warning {
        background: #ffc107;
        color: black;
      }
      button:disabled {
        opacity: 0.6;
        cursor: not-allowed;
      }
      input {
        padding: 8px;
        border: 1px solid #ddd;
        border-radius: 4px;
        margin: 5px;
      }
      .connected {
        color: #28a745;
      }
      .disconnected {
        color: #dc3545;
      }
      .connecting {
        color: #ffc107;
      }
      .metric {
        display: flex;
        justify-content: space-between;
        margin: 5px 0;
      }
    </style>
  </head>
  <body>
    <div class="container">
      <h1>WebSocket 详细调试工具</h1>

      <div class="status-panel">
        <div class="status-card">
          <h3>连接状态</h3>
          <div class="metric">
            <span>状态:</span>
            <span id="connectionStatus" class="disconnected">未连接</span>
          </div>
          <div class="metric">
            <span>连接时长:</span>
            <span id="connectionTime">0秒</span>
          </div>
          <div class="metric">
            <span>客户端ID:</span>
            <span id="clientId">-</span>
          </div>
        </div>

        <div class="status-card">
          <h3>消息统计</h3>
          <div class="metric">
            <span>发送消息:</span>
            <span id="sentCount">0</span>
          </div>
          <div class="metric">
            <span>接收消息:</span>
            <span id="receivedCount">0</span>
          </div>
          <div class="metric">
            <span>错误次数:</span>
            <span id="errorCount">0</span>
          </div>
        </div>

        <div class="status-card">
          <h3>连接详情</h3>
          <div class="metric">
            <span>协议:</span>
            <span id="protocol">-</span>
          </div>
          <div class="metric">
            <span>就绪状态:</span>
            <span id="readyState">-</span>
          </div>
          <div class="metric">
            <span>扩展:</span>
            <span id="extensions">-</span>
          </div>
        </div>
      </div>

      <div class="controls">
        <input
          type="text"
          id="wsUrl"
          value="ws://localhost:3003/ws"
          style="width: 300px"
          placeholder="WebSocket URL"
        />
        <button id="connectBtn" class="btn-primary">连接</button>
        <button id="disconnectBtn" class="btn-danger" disabled>断开</button>
        <button id="pingBtn" class="btn-success" disabled>发送心跳</button>
        <button id="clearBtn" class="btn-warning">清空日志</button>
      </div>

      <div class="controls">
        <input
          type="text"
          id="messageInput"
          placeholder="输入测试消息"
          style="width: 300px"
        />
        <button id="sendBtn" class="btn-primary" disabled>发送消息</button>
        <button id="stressTestBtn" class="btn-warning" disabled>
          压力测试
        </button>
      </div>

      <div class="log" id="log"></div>
    </div>

    <script>
      class WebSocketDebugger {
        constructor() {
          this.socket = null;
          this.startTime = null;
          this.sentCount = 0;
          this.receivedCount = 0;
          this.errorCount = 0;
          this.heartbeatInterval = null;
          this.connectionCheckInterval = null;

          this.initElements();
          this.bindEvents();
          this.startConnectionMonitoring();
        }

        initElements() {
          this.elements = {
            wsUrl: document.getElementById("wsUrl"),
            connectBtn: document.getElementById("connectBtn"),
            disconnectBtn: document.getElementById("disconnectBtn"),
            pingBtn: document.getElementById("pingBtn"),
            clearBtn: document.getElementById("clearBtn"),
            sendBtn: document.getElementById("sendBtn"),
            stressTestBtn: document.getElementById("stressTestBtn"),
            messageInput: document.getElementById("messageInput"),
            log: document.getElementById("log"),
            connectionStatus: document.getElementById("connectionStatus"),
            connectionTime: document.getElementById("connectionTime"),
            clientId: document.getElementById("clientId"),
            sentCount: document.getElementById("sentCount"),
            receivedCount: document.getElementById("receivedCount"),
            errorCount: document.getElementById("errorCount"),
            protocol: document.getElementById("protocol"),
            readyState: document.getElementById("readyState"),
            extensions: document.getElementById("extensions"),
          };
        }

        bindEvents() {
          this.elements.connectBtn.addEventListener("click", () =>
            this.connect()
          );
          this.elements.disconnectBtn.addEventListener("click", () =>
            this.disconnect()
          );
          this.elements.pingBtn.addEventListener("click", () =>
            this.sendPing()
          );
          this.elements.clearBtn.addEventListener("click", () =>
            this.clearLog()
          );
          this.elements.sendBtn.addEventListener("click", () =>
            this.sendMessage()
          );
          this.elements.stressTestBtn.addEventListener("click", () =>
            this.stressTest()
          );

          this.elements.messageInput.addEventListener("keypress", (e) => {
            if (e.key === "Enter") this.sendMessage();
          });
        }

        addLog(message, type = "info") {
          const timestamp = new Date().toLocaleTimeString();
          const colors = {
            info: "#0f0",
            error: "#f00",
            warning: "#ff0",
            success: "#0f0",
          };

          const div = document.createElement("div");
          div.style.color = colors[type] || "#0f0";
          div.textContent = `[${timestamp}] ${message}`;
          this.elements.log.appendChild(div);
          this.elements.log.scrollTop = this.elements.log.scrollHeight;
        }

        updateStatus(status, className) {
          this.elements.connectionStatus.textContent = status;
          this.elements.connectionStatus.className = className;
        }

        updateConnectionDetails() {
          if (this.socket) {
            this.elements.protocol.textContent = this.socket.protocol || "未知";
            this.elements.readyState.textContent = this.getReadyStateText(
              this.socket.readyState
            );
            this.elements.extensions.textContent =
              this.socket.extensions || "无";
          } else {
            this.elements.protocol.textContent = "-";
            this.elements.readyState.textContent = "-";
            this.elements.extensions.textContent = "-";
          }
        }

        getReadyStateText(state) {
          const states = {
            0: "CONNECTING",
            1: "OPEN",
            2: "CLOSING",
            3: "CLOSED",
          };
          return `${states[state]} (${state})`;
        }

        connect() {
          const url = this.elements.wsUrl.value.trim();
          if (!url) {
            this.addLog("请输入WebSocket URL", "error");
            return;
          }

          this.addLog(`尝试连接到: ${url}`, "info");
          this.updateStatus("连接中...", "connecting");

          try {
            this.socket = new WebSocket(url);
            this.setupSocketEvents();
          } catch (error) {
            this.addLog(`连接失败: ${error.message}`, "error");
            this.updateStatus("连接失败", "disconnected");
            this.errorCount++;
            this.updateStats();
          }
        }

        setupSocketEvents() {
          this.socket.onopen = (event) => {
            this.addLog("✅ WebSocket 连接成功!", "success");
            this.updateStatus("已连接", "connected");
            this.startTime = Date.now();

            this.elements.connectBtn.disabled = true;
            this.elements.disconnectBtn.disabled = false;
            this.elements.pingBtn.disabled = false;
            this.elements.sendBtn.disabled = false;
            this.elements.stressTestBtn.disabled = false;

            this.updateConnectionDetails();
            this.startHeartbeat();
          };

          this.socket.onmessage = (event) => {
            this.receivedCount++;
            this.updateStats();

            try {
              const data = JSON.parse(event.data);
              this.addLog(
                `📩 收到消息: ${JSON.stringify(data, null, 2)}`,
                "info"
              );

              if (data.type === "connected") {
                this.elements.clientId.textContent = data.clientId;
                this.addLog(`🆔 客户端ID: ${data.clientId}`, "success");
              }
            } catch (error) {
              this.addLog(`📩 收到消息 (原始): ${event.data}`, "info");
            }
          };

          this.socket.onclose = (event) => {
            this.addLog(
              `❌ 连接关闭: 代码=${event.code}, 原因="${event.reason}"`,
              "error"
            );
            this.addLog(`关闭详情: wasClean=${event.wasClean}`, "warning");
            this.updateStatus("已断开", "disconnected");
            this.resetConnection();

            // 分析关闭代码
            this.analyzeCloseCode(event.code);
          };

          this.socket.onerror = (event) => {
            this.errorCount++;
            this.updateStats();
            this.addLog(`💥 WebSocket 错误事件触发`, "error");
            this.addLog(`错误详情: ${JSON.stringify(event)}`, "error");
            this.updateStatus("连接错误", "disconnected");
          };
        }

        analyzeCloseCode(code) {
          const codes = {
            1000: "正常关闭",
            1001: "端点离开",
            1002: "协议错误",
            1003: "不支持的数据类型",
            1004: "保留",
            1005: "未收到状态码",
            1006: "异常关闭 - 连接意外中断",
            1007: "数据类型错误",
            1008: "违反策略",
            1009: "消息过大",
            1010: "扩展协商失败",
            1011: "服务器错误",
            1012: "服务重启",
            1013: "稍后重试",
            1014: "网关错误",
            1015: "TLS握手失败",
          };

          const meaning = codes[code] || "未知错误";
          this.addLog(`🔍 关闭代码分析: ${code} - ${meaning}`, "warning");

          if (code === 1006) {
            this.addLog("💡 1006错误建议:", "warning");
            this.addLog("  - 检查网络连接", "warning");
            this.addLog("  - 检查防火墙设置", "warning");
            this.addLog("  - 检查服务器是否正常运行", "warning");
            this.addLog("  - 检查浏览器控制台是否有其他错误", "warning");
          }
        }

        disconnect() {
          if (this.socket) {
            this.addLog("手动断开连接", "info");
            this.socket.close(1000, "用户主动断开");
          }
        }

        resetConnection() {
          this.socket = null;
          this.startTime = null;
          this.elements.clientId.textContent = "-";

          this.elements.connectBtn.disabled = false;
          this.elements.disconnectBtn.disabled = true;
          this.elements.pingBtn.disabled = true;
          this.elements.sendBtn.disabled = true;
          this.elements.stressTestBtn.disabled = true;

          this.stopHeartbeat();
          this.updateConnectionDetails();
        }

        sendMessage() {
          const message = this.elements.messageInput.value.trim();
          if (!message) return;

          if (this.socket && this.socket.readyState === WebSocket.OPEN) {
            const testMessage = {
              type: "test",
              message: message,
              timestamp: new Date().toISOString(),
            };

            this.socket.send(JSON.stringify(testMessage));
            this.sentCount++;
            this.updateStats();
            this.addLog(`📤 发送消息: ${JSON.stringify(testMessage)}`, "info");
            this.elements.messageInput.value = "";
          } else {
            this.addLog("❌ WebSocket 未连接", "error");
          }
        }

        sendPing() {
          if (this.socket && this.socket.readyState === WebSocket.OPEN) {
            const pingMessage = {
              type: "ping",
              timestamp: Date.now(),
            };

            this.socket.send(JSON.stringify(pingMessage));
            this.sentCount++;
            this.updateStats();
            this.addLog("🏓 发送心跳包", "info");
          } else {
            this.addLog("❌ 无法发送心跳: WebSocket 未连接", "error");
          }
        }

        startHeartbeat() {
          this.heartbeatInterval = setInterval(() => {
            this.sendPing();
          }, 30000); // 30秒心跳
        }

        stopHeartbeat() {
          if (this.heartbeatInterval) {
            clearInterval(this.heartbeatInterval);
            this.heartbeatInterval = null;
          }
        }

        stressTest() {
          if (this.socket && this.socket.readyState === WebSocket.OPEN) {
            this.addLog("🧪 开始压力测试 (发送10条消息)", "warning");

            for (let i = 0; i < 10; i++) {
              setTimeout(() => {
                const message = {
                  type: "stress-test",
                  index: i + 1,
                  timestamp: Date.now(),
                };
                this.socket.send(JSON.stringify(message));
                this.sentCount++;
                this.updateStats();
              }, i * 100);
            }
          } else {
            this.addLog("❌ 无法进行压力测试: WebSocket 未连接", "error");
          }
        }

        clearLog() {
          this.elements.log.innerHTML = "";
          this.addLog("日志已清空", "info");
        }

        updateStats() {
          this.elements.sentCount.textContent = this.sentCount;
          this.elements.receivedCount.textContent = this.receivedCount;
          this.elements.errorCount.textContent = this.errorCount;
        }

        startConnectionMonitoring() {
          this.connectionCheckInterval = setInterval(() => {
            if (this.startTime) {
              const duration = Math.floor((Date.now() - this.startTime) / 1000);
              this.elements.connectionTime.textContent = `${duration}秒`;
            }

            this.updateConnectionDetails();
          }, 1000);
        }
      }

      // 启动调试器
      window.addEventListener("DOMContentLoaded", () => {
        new WebSocketDebugger();
      });
    </script>
  </body>
</html>
